{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from sklearn.utils import shuffle\n",
    "import re\n",
    "import time\n",
    "import collections\n",
    "import os"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def build_dataset(words, n_words, atleast=1):\n",
    "    count = [['PAD', 0], ['GO', 1], ['EOS', 2], ['UNK', 3]]\n",
    "    counter = collections.Counter(words).most_common(n_words)\n",
    "    counter = [i for i in counter if i[1] >= atleast]\n",
    "    count.extend(counter)\n",
    "    dictionary = dict()\n",
    "    for word, _ in count:\n",
    "        dictionary[word] = len(dictionary)\n",
    "    data = list()\n",
    "    unk_count = 0\n",
    "    for word in words:\n",
    "        index = dictionary.get(word, 0)\n",
    "        if index == 0:\n",
    "            unk_count += 1\n",
    "        data.append(index)\n",
    "    count[0][1] = unk_count\n",
    "    reversed_dictionary = dict(zip(dictionary.values(), dictionary.keys()))\n",
    "    return data, count, dictionary, reversed_dictionary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "lines = open('movie_lines.txt', encoding='utf-8', errors='ignore').read().split('\\n')\n",
    "conv_lines = open('movie_conversations.txt', encoding='utf-8', errors='ignore').read().split('\\n')\n",
    "\n",
    "id2line = {}\n",
    "for line in lines:\n",
    "    _line = line.split(' +++$+++ ')\n",
    "    if len(_line) == 5:\n",
    "        id2line[_line[0]] = _line[4]\n",
    "        \n",
    "convs = [ ]\n",
    "for line in conv_lines[:-1]:\n",
    "    _line = line.split(' +++$+++ ')[-1][1:-1].replace(\"'\",\"\").replace(\" \",\"\")\n",
    "    convs.append(_line.split(','))\n",
    "    \n",
    "questions = []\n",
    "answers = []\n",
    "\n",
    "for conv in convs:\n",
    "    for i in range(len(conv)-1):\n",
    "        questions.append(id2line[conv[i]])\n",
    "        answers.append(id2line[conv[i+1]])\n",
    "        \n",
    "def clean_text(text):\n",
    "    text = text.lower()\n",
    "    text = re.sub(r\"i'm\", \"i am\", text)\n",
    "    text = re.sub(r\"he's\", \"he is\", text)\n",
    "    text = re.sub(r\"she's\", \"she is\", text)\n",
    "    text = re.sub(r\"it's\", \"it is\", text)\n",
    "    text = re.sub(r\"that's\", \"that is\", text)\n",
    "    text = re.sub(r\"what's\", \"that is\", text)\n",
    "    text = re.sub(r\"where's\", \"where is\", text)\n",
    "    text = re.sub(r\"how's\", \"how is\", text)\n",
    "    text = re.sub(r\"\\'ll\", \" will\", text)\n",
    "    text = re.sub(r\"\\'ve\", \" have\", text)\n",
    "    text = re.sub(r\"\\'re\", \" are\", text)\n",
    "    text = re.sub(r\"\\'d\", \" would\", text)\n",
    "    text = re.sub(r\"\\'re\", \" are\", text)\n",
    "    text = re.sub(r\"won't\", \"will not\", text)\n",
    "    text = re.sub(r\"can't\", \"cannot\", text)\n",
    "    text = re.sub(r\"n't\", \" not\", text)\n",
    "    text = re.sub(r\"n'\", \"ng\", text)\n",
    "    text = re.sub(r\"'bout\", \"about\", text)\n",
    "    text = re.sub(r\"'til\", \"until\", text)\n",
    "    text = re.sub(r\"[-()\\\"#/@;:<>{}`+=~|.!?,]\", \"\", text)\n",
    "    return ' '.join([i.strip() for i in filter(None, text.split())])\n",
    "\n",
    "clean_questions = []\n",
    "for question in questions:\n",
    "    clean_questions.append(clean_text(question))\n",
    "    \n",
    "clean_answers = []    \n",
    "for answer in answers:\n",
    "    clean_answers.append(clean_text(answer))\n",
    "    \n",
    "min_line_length = 2\n",
    "max_line_length = 5\n",
    "short_questions_temp = []\n",
    "short_answers_temp = []\n",
    "\n",
    "i = 0\n",
    "for question in clean_questions:\n",
    "    if len(question.split()) >= min_line_length and len(question.split()) <= max_line_length:\n",
    "        short_questions_temp.append(question)\n",
    "        short_answers_temp.append(clean_answers[i])\n",
    "    i += 1\n",
    "\n",
    "short_questions = []\n",
    "short_answers = []\n",
    "\n",
    "i = 0\n",
    "for answer in short_answers_temp:\n",
    "    if len(answer.split()) >= min_line_length and len(answer.split()) <= max_line_length:\n",
    "        short_answers.append(answer)\n",
    "        short_questions.append(short_questions_temp[i])\n",
    "    i += 1\n",
    "\n",
    "question_test = short_questions[500:550]\n",
    "answer_test = short_answers[500:550]\n",
    "short_questions = short_questions[:500]\n",
    "short_answers = short_answers[:500]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab from size: 657\n",
      "Most common words [('you', 132), ('is', 78), ('i', 68), ('what', 51), ('it', 50), ('that', 49)]\n",
      "Sample data [7, 28, 129, 35, 61, 42, 12, 22, 82, 225] ['what', 'good', 'stuff', 'she', 'okay', 'they', 'do', 'to', 'hey', 'sweet']\n",
      "filtered vocab size: 661\n",
      "% of vocab used: 100.61%\n"
     ]
    }
   ],
   "source": [
    "concat_from = ' '.join(short_questions+question_test).split()\n",
    "vocabulary_size_from = len(list(set(concat_from)))\n",
    "data_from, count_from, dictionary_from, rev_dictionary_from = build_dataset(concat_from, vocabulary_size_from)\n",
    "print('vocab from size: %d'%(vocabulary_size_from))\n",
    "print('Most common words', count_from[4:10])\n",
    "print('Sample data', data_from[:10], [rev_dictionary_from[i] for i in data_from[:10]])\n",
    "print('filtered vocab size:',len(dictionary_from))\n",
    "print(\"% of vocab used: {}%\".format(round(len(dictionary_from)/vocabulary_size_from,4)*100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab from size: 660\n",
      "Most common words [('i', 97), ('you', 91), ('is', 62), ('it', 58), ('not', 47), ('what', 39)]\n",
      "Sample data [12, 216, 5, 4, 94, 25, 59, 10, 8, 79] ['the', 'real', 'you', 'i', 'hope', 'so', 'they', 'do', 'not', 'hi']\n",
      "filtered vocab size: 664\n",
      "% of vocab used: 100.61%\n"
     ]
    }
   ],
   "source": [
    "concat_to = ' '.join(short_answers+answer_test).split()\n",
    "vocabulary_size_to = len(list(set(concat_to)))\n",
    "data_to, count_to, dictionary_to, rev_dictionary_to = build_dataset(concat_to, vocabulary_size_to)\n",
    "print('vocab from size: %d'%(vocabulary_size_to))\n",
    "print('Most common words', count_to[4:10])\n",
    "print('Sample data', data_to[:10], [rev_dictionary_to[i] for i in data_to[:10]])\n",
    "print('filtered vocab size:',len(dictionary_to))\n",
    "print(\"% of vocab used: {}%\".format(round(len(dictionary_to)/vocabulary_size_to,4)*100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "GO = dictionary_from['GO']\n",
    "PAD = dictionary_from['PAD']\n",
    "EOS = dictionary_from['EOS']\n",
    "UNK = dictionary_from['UNK']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(len(short_answers)):\n",
    "    short_answers[i] += ' EOS'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def str_idx(corpus, dic):\n",
    "    X = []\n",
    "    for i in corpus:\n",
    "        ints = []\n",
    "        for k in i.split():\n",
    "            ints.append(dic.get(k,UNK))\n",
    "        X.append(ints)\n",
    "    return X\n",
    "\n",
    "def pad_sentence_batch(sentence_batch, pad_int, maxlen):\n",
    "    padded_seqs = []\n",
    "    seq_lens = []\n",
    "    max_sentence_len = maxlen\n",
    "    for sentence in sentence_batch:\n",
    "        padded_seqs.append(sentence + [pad_int] * (max_sentence_len - len(sentence)))\n",
    "        seq_lens.append(maxlen)\n",
    "    return padded_seqs, seq_lens"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = str_idx(short_questions, dictionary_from)\n",
    "Y = str_idx(short_answers, dictionary_to)\n",
    "X_test = str_idx(question_test, dictionary_from)\n",
    "Y_test = str_idx(answer_test, dictionary_from)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "maxlen_question = max([len(x) for x in X]) * 2\n",
    "maxlen_answer = max([len(y) for y in Y]) * 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def layer_normalization(x, epsilon=1e-8):\n",
    "    shape = x.get_shape()\n",
    "    tf.Variable(tf.zeros(shape = [int(shape[-1])]))\n",
    "    beta = tf.Variable(tf.zeros(shape = [int(shape[-1])]))\n",
    "    gamma = tf.Variable(tf.ones(shape = [int(shape[-1])]))\n",
    "    mean, variance = tf.nn.moments(x, axes=[len(shape) - 1], keep_dims=True)\n",
    "    x = (x - mean) /  tf.sqrt(variance + epsilon)\n",
    "    return gamma * x + beta\n",
    "\n",
    "def conv1d(input_, output_channels, dilation = 1, filter_width = 1, causal = False):\n",
    "    w = tf.Variable(tf.random_normal([1, filter_width, int(input_.get_shape()[-1]), output_channels], stddev = 0.02))\n",
    "    b = tf.Variable(tf.zeros(shape = [output_channels]))\n",
    "    if causal:\n",
    "        padding = [[0, 0], [(filter_width - 1) * dilation, 0], [0, 0]]\n",
    "        padded = tf.pad(input_, padding)\n",
    "        input_expanded = tf.expand_dims(padded, dim = 1)\n",
    "        out = tf.nn.atrous_conv2d(input_expanded, w, rate = dilation, padding = 'VALID') + b\n",
    "    else:\n",
    "        input_expanded = tf.expand_dims(input_, dim = 1)\n",
    "        out = tf.nn.atrous_conv2d(input_expanded, w, rate = dilation, padding = 'SAME') + b\n",
    "    return tf.squeeze(out, [1])\n",
    "\n",
    "def bytenet_residual_block(input_, dilation, layer_no, \n",
    "                            residual_channels, filter_width, \n",
    "                            causal = True):\n",
    "    block_type = \"decoder\" if causal else \"encoder\"\n",
    "    block_name = \"bytenet_{}_layer_{}_{}\".format(block_type, layer_no, dilation)\n",
    "    with tf.variable_scope(block_name):\n",
    "        relu1 = tf.nn.relu(layer_normalization(input_))\n",
    "        conv1 = conv1d(relu1, residual_channels)\n",
    "        relu2 = tf.nn.relu(layer_normalization(conv1))\n",
    "        dilated_conv = conv1d(relu2, residual_channels, \n",
    "                              dilation, filter_width,\n",
    "                              causal = causal)\n",
    "        print(dilated_conv)\n",
    "        relu3 = tf.nn.relu(layer_normalization(dilated_conv))\n",
    "        conv2 = conv1d(relu3, 2 * residual_channels)\n",
    "        return input_ + conv2\n",
    "    \n",
    "class ByteNet:\n",
    "    def __init__(self, from_vocab_size, to_vocab_size, channels, encoder_dilations,\n",
    "                decoder_dilations, encoder_filter_width, decoder_filter_width,\n",
    "                learning_rate = 0.001, beta1=0.5):\n",
    "        self.X = tf.placeholder(tf.int32, [None, None])\n",
    "        self.Y = tf.placeholder(tf.int32, [None, None])\n",
    "        self.X_seq_len = tf.count_nonzero(self.X, 1, dtype = tf.int32)\n",
    "        self.Y_seq_len = tf.count_nonzero(self.Y, 1, dtype = tf.int32)\n",
    "        batch_size = tf.shape(self.X)[0]\n",
    "        main = tf.strided_slice(self.Y, [0, 0], [batch_size, -1], [1, 1])\n",
    "        target_1 = tf.concat([tf.fill([batch_size, 1], GO), main], 1)\n",
    "        embedding_channels = 2 * channels\n",
    "        w_source_embedding = tf.Variable(tf.random_normal([from_vocab_size, \n",
    "                                                           embedding_channels], stddev = 0.02))\n",
    "        w_target_embedding = tf.Variable(tf.random_normal([to_vocab_size, \n",
    "                                                           embedding_channels], stddev = 0.02))\n",
    "        source_embedding = tf.nn.embedding_lookup(w_source_embedding, self.X)\n",
    "        target_1_embedding = tf.nn.embedding_lookup(w_target_embedding, target_1)\n",
    "        curr_input = source_embedding\n",
    "        for layer_no, dilation in enumerate(encoder_dilations):\n",
    "            curr_input = bytenet_residual_block(curr_input, dilation, \n",
    "                                                layer_no, channels, \n",
    "                                                encoder_filter_width, \n",
    "                                                causal = False)\n",
    "        encoder_output = curr_input\n",
    "        combined_embedding = target_1_embedding + encoder_output\n",
    "        curr_input = combined_embedding\n",
    "        for layer_no, dilation in enumerate(decoder_dilations):\n",
    "            curr_input = bytenet_residual_block(curr_input, dilation, \n",
    "                                                layer_no, channels, \n",
    "                                                encoder_filter_width, \n",
    "                                                causal = False)\n",
    "        self.logits = conv1d(curr_input, to_vocab_size)\n",
    "        masks = tf.sequence_mask(self.Y_seq_len, maxlen_answer, dtype=tf.float32)\n",
    "        self.cost = tf.contrib.seq2seq.sequence_loss(logits = self.logits,\n",
    "                                                     targets = self.Y,\n",
    "                                                     weights = masks)\n",
    "        self.optimizer = tf.train.AdamOptimizer(learning_rate).minimize(self.cost)\n",
    "        y_t = tf.argmax(self.logits,axis=2)\n",
    "        y_t = tf.cast(y_t, tf.int32)\n",
    "        self.prediction = tf.boolean_mask(y_t, masks)\n",
    "        mask_label = tf.boolean_mask(self.Y, masks)\n",
    "        correct_pred = tf.equal(self.prediction, mask_label)\n",
    "        correct_index = tf.cast(correct_pred, tf.float32)\n",
    "        self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "residual_channels = 128\n",
    "encoder_dilations = [1,2,4,8,16,1,2,4,8,16]\n",
    "decoder_dilations = [1,2,4,8,16,1,2,4,8,16]\n",
    "encoder_filter_width = 3\n",
    "decoder_filter_width = 3\n",
    "batch_size = 16\n",
    "epoch = 20"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From <ipython-input-11-b52bb1b49203>:19: calling expand_dims (from tensorflow.python.ops.array_ops) with dim is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use the `axis` argument instead\n",
      "Tensor(\"bytenet_encoder_layer_0_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_1_2/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_2_4/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_3_8/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_4_16/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_5_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_6_2/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_7_4/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_8_8/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_9_16/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_0_1_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_1_2_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_2_4_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_3_8_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_4_16_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_5_1_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_6_2_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_7_4_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_8_8_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n",
      "Tensor(\"bytenet_encoder_layer_9_16_1/Squeeze_1:0\", shape=(?, ?, 128), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "tf.reset_default_graph()\n",
    "sess = tf.InteractiveSession()\n",
    "model = ByteNet(len(dictionary_from), len(dictionary_to), \n",
    "                residual_channels, encoder_dilations, decoder_dilations,\n",
    "                encoder_filter_width,decoder_filter_width)\n",
    "sess.run(tf.global_variables_initializer())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 1, avg loss: 5.346178, avg accuracy: 0.237054\n",
      "epoch: 2, avg loss: 4.796436, avg accuracy: 0.266569\n",
      "epoch: 3, avg loss: 4.531795, avg accuracy: 0.271688\n",
      "epoch: 4, avg loss: 4.221749, avg accuracy: 0.281147\n",
      "epoch: 5, avg loss: 4.081553, avg accuracy: 0.282542\n",
      "epoch: 6, avg loss: 4.020089, avg accuracy: 0.299463\n",
      "epoch: 7, avg loss: 3.717178, avg accuracy: 0.306720\n",
      "epoch: 8, avg loss: 3.412678, avg accuracy: 0.340576\n",
      "epoch: 9, avg loss: 3.273545, avg accuracy: 0.354941\n",
      "epoch: 10, avg loss: 3.008131, avg accuracy: 0.379655\n",
      "epoch: 11, avg loss: 2.789968, avg accuracy: 0.423567\n",
      "epoch: 12, avg loss: 2.449771, avg accuracy: 0.470402\n",
      "epoch: 13, avg loss: 2.150040, avg accuracy: 0.512227\n",
      "epoch: 14, avg loss: 1.930665, avg accuracy: 0.564058\n",
      "epoch: 15, avg loss: 1.672674, avg accuracy: 0.623102\n",
      "epoch: 16, avg loss: 1.299433, avg accuracy: 0.709427\n",
      "epoch: 17, avg loss: 1.029263, avg accuracy: 0.775196\n",
      "epoch: 18, avg loss: 0.810936, avg accuracy: 0.816506\n",
      "epoch: 19, avg loss: 0.671860, avg accuracy: 0.856233\n",
      "epoch: 20, avg loss: 0.579977, avg accuracy: 0.877510\n"
     ]
    }
   ],
   "source": [
    "for i in range(epoch):\n",
    "    total_loss, total_accuracy = 0, 0\n",
    "    for k in range(0, len(short_questions), batch_size):\n",
    "        index = min(k+batch_size, len(short_questions))\n",
    "        batch_x, seq_x = pad_sentence_batch(X[k: index], PAD, maxlen_answer)\n",
    "        batch_y, seq_y = pad_sentence_batch(Y[k: index], PAD, maxlen_answer)\n",
    "        predicted, accuracy,loss, _ = sess.run([model.logits, \n",
    "                                                model.accuracy, model.cost, model.optimizer], \n",
    "                                      feed_dict={model.X:batch_x,\n",
    "                                                model.Y:batch_y})\n",
    "        total_loss += loss\n",
    "        total_accuracy += accuracy\n",
    "    total_loss /= (len(short_questions) / batch_size)\n",
    "    total_accuracy /= (len(short_questions) / batch_size)\n",
    "    print('epoch: %d, avg loss: %f, avg accuracy: %f'%(i+1, total_loss, total_accuracy))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "row 1\n",
      "QUESTION: i am a werewolf\n",
      "REAL ANSWER: a werewolf\n",
      "PREDICTED ANSWER: a werewolf sox did you north lit \n",
      "\n",
      "row 2\n",
      "QUESTION: i was dreaming again\n",
      "REAL ANSWER: i would think so\n",
      "PREDICTED ANSWER: i would think so is is lit \n",
      "\n",
      "row 3\n",
      "QUESTION: the kitchen\n",
      "REAL ANSWER: very nice\n",
      "PREDICTED ANSWER: very nice you lit \n",
      "\n",
      "row 4\n",
      "QUESTION: the bedroom\n",
      "REAL ANSWER: there is only one bed\n",
      "PREDICTED ANSWER: there is only one bed ask you lit \n",
      "\n"
     ]
    }
   ],
   "source": [
    "predicted = np.argmax(predicted, axis = 2)\n",
    "\n",
    "for i in range(len(batch_x)):\n",
    "    print('row %d'%(i+1))\n",
    "    print('QUESTION:',' '.join([rev_dictionary_from[n] for n in batch_x[i] if n not in [0,1,2,3]]))\n",
    "    print('REAL ANSWER:',' '.join([rev_dictionary_to[n] for n in batch_y[i] if n not in[0,1,2,3]]))\n",
    "    print('PREDICTED ANSWER:',' '.join([rev_dictionary_to[n] for n in predicted[i] if n not in[0,1,2,3]]),'\\n')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
